package org.xx.armory.spring5.mvc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.dao.DataAccessException;
import org.springframework.web.context.request.WebRequest;
import org.xx.armory.commons.ForLogging;

import java.sql.SQLException;
import java.util.Map;

import static org.xx.armory.commons.DebugUtils.dumpTraceBack;

public class ArmoryErrorAttributes
        extends DefaultErrorAttributes
        implements ErrorAttributes {
    public static final String ERROR_TRACE_KEY = "trace";
    public static final String ERROR_CODE_KEY = "code";
    public static final String ERROR_EXTRAS_KEY = "extras";
    public static final String ERROR_SQL_STATE_KEY = "sqlState";
    public static final String ERROR_SQL_CODE_KEY = "sqlErrorCode";

    @ForLogging
    private final Logger logger = LoggerFactory.getLogger(ArmoryErrorAttributes.class);

    @Override
    public Map<String, Object> getErrorAttributes(
            WebRequest webRequest,
            ErrorAttributeOptions options
    ) {
        final var result = super.getErrorAttributes(webRequest, options);

        final var error = getError(webRequest);

        if (error != null) {
            if (options.isIncluded(ErrorAttributeOptions.Include.STACK_TRACE)) {
                result.put(ERROR_TRACE_KEY, dumpTraceBack(error));
            }

            if (error instanceof BusinessLogicException) {
                final var busEx = (BusinessLogicException) error;
                result.put(ERROR_CODE_KEY, busEx.getCode());
                result.put(ERROR_EXTRAS_KEY, busEx.getExtras());
            } else if (error instanceof SQLException) {
                final var sqlEx = (SQLException) error;
                final var sqlState = sqlEx.getSQLState();
                result.put(ERROR_SQL_STATE_KEY, sqlState != null ? sqlState : "");
                result.put(ERROR_SQL_CODE_KEY, sqlEx.getErrorCode());
            } else if (error instanceof DataAccessException) {
                var se = error.getCause();
                while (se != null) {
                    if (se instanceof SQLException) {
                        final var sqlEx = (SQLException) se;
                        final var sqlState = sqlEx.getSQLState();
                        result.put(ERROR_SQL_STATE_KEY, sqlState != null ? sqlState : "");
                        result.put(ERROR_SQL_CODE_KEY, sqlEx.getErrorCode());
                        break;
                    } else {
                        se = se.getCause();
                    }
                }
            }
        }

        return result;
    }
}
